/*
 * Generate calendar week data with monthStartWeekIndex/monthEndWeekIndex.
 * Weeks start on Sunday (0) and end on Saturday (6).
 * Usage: node tools/generateCalendarData.js 2020 2030 "entry/src/main/resources/rawfile/calendar_data copy.json"
 */

const fs = require('fs');
const path = require('path');

function toDateYMD(date) {
  return { year: date.getFullYear(), month: date.getMonth() + 1, day: date.getDate() };
}

function getSundayOfWeek(d) {
  const date = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate()));
  const day = date.getUTCDay(); // 0..6, 0=Sunday
  const sunday = new Date(date);
  sunday.setUTCDate(date.getUTCDate() - day);
  return sunday;
}

function getSaturdayOfWeek(d) {
  const sunday = getSundayOfWeek(d);
  const saturday = new Date(sunday);
  saturday.setUTCDate(sunday.getUTCDate() + 6);
  return saturday;
}

function* dateRangeInclusive(start, end) {
  const d = new Date(start);
  while (d <= end) {
    yield new Date(d);
    d.setUTCDate(d.getUTCDate() + 1);
  }
}

function buildWeeks(startYear, endYear) {
  const startOfSpan = new Date(Date.UTC(startYear, 0, 1)); // Jan 1 UTC
  const endOfSpan = new Date(Date.UTC(endYear, 11, 31)); // Dec 31 UTC

  const firstSunday = getSundayOfWeek(startOfSpan);
  const lastSaturday = getSaturdayOfWeek(endOfSpan);

  // Build continuous weeks (arrays of 7 dates)
  const weeks = [];
  let current = new Date(firstSunday);
  while (current <= lastSaturday) {
    const week = [];
    for (let i = 0; i < 7; i++) {
      week.push(new Date(current));
      current.setUTCDate(current.getUTCDate() + 1);
    }
    weeks.push(week);
  }
  return { weeks, firstSunday, lastSaturday };
}

function daysInMonthUTC(year, month1to12) {
  return new Date(Date.UTC(year, month1to12, 0)).getUTCDate();
}

function computeMonthIndicesAccumulative(weeks) {
  // Build month indices from the month of the first SUNDAY (could be prior month)
  // using the user's rule:
  // - count = number of weeks that month spans (Sunday-Saturday weeks)
  // - monthEndWeekIndex = monthStartWeekIndex + count
  // - next.monthStartWeekIndex = prev.monthEndWeekIndex - 1 if cross-month week, else prev.monthEndWeekIndex
  if (weeks.length === 0) return new Map();
  const firstDate = weeks[0][0]; // first Sunday
  const lastDate = weeks[weeks.length - 1][6]; // last Saturday

  // Start from the month of the first Sunday
  let y = firstDate.getUTCFullYear();
  let m = firstDate.getUTCMonth() + 1; // 1..12

  const endY = lastDate.getUTCFullYear();
  const endM = lastDate.getUTCMonth() + 1;

  // Helper: advance month
  function incMonth(year, month) {
    if (month === 12) return [year + 1, 1];
    return [year, month + 1];
  }

  // Compute week index of a given date (yyyy-mm-dd) relative to weeks array
  const dateToWeekIndex = new Map();
  for (let w = 0; w < weeks.length; w++) {
    for (let i = 0; i < 7; i++) {
      const d = weeks[w][i];
      const key = `${d.getUTCFullYear()}-${d.getUTCMonth() + 1}-${d.getUTCDate()}`;
      dateToWeekIndex.set(key, w);
    }
  }

  function sundayOnOrBefore(year, month, day) {
    const d = new Date(Date.UTC(year, month - 1, day));
    return getSundayOfWeek(d);
  }

  function saturdayOnOrAfter(year, month, day) {
    const d = new Date(Date.UTC(year, month - 1, day));
    return getSaturdayOfWeek(d);
  }

  const indices = new Map(); // key: y-m
  let startIndex = 0; // start from 0 for the first month (month of first Sunday)

  while (y < endY || (y === endY && m <= endM)) {
    const dim = daysInMonthUTC(y, m);
    const firstSunday = sundayOnOrBefore(y, m, 1);
    const lastSaturday = saturdayOnOrAfter(y, m, dim);
    const firstKey = `${firstSunday.getUTCFullYear()}-${firstSunday.getUTCMonth() + 1}-${firstSunday.getUTCDate()}`;
    const lastKey = `${lastSaturday.getUTCFullYear()}-${lastSaturday.getUTCMonth() + 1}-${lastSaturday.getUTCDate()}`;
    const firstWeekIdx = dateToWeekIndex.get(firstKey);
    const lastWeekIdx = dateToWeekIndex.get(lastKey);

    // If either week lies outside generated span, infer using arithmetic relative to current startIndex
    let count;
    if (firstWeekIdx != null && lastWeekIdx != null) {
      count = (lastWeekIdx - firstWeekIdx) + 1;
    } else {
      // Fallback formula independent of data span: weeks spanned by the month
      // first weekday (0=Sun) and number of days determine weeks count
      const firstDay = new Date(Date.UTC(y, m - 1, 1));
      const firstWeekday = firstDay.getUTCDay();
      count = Math.ceil((firstWeekday + dim) / 7);
    }

    const endIndex = startIndex + count; // exclusive
    indices.set(`${y}-${m}`, { startWeekIndex: startIndex, endWeekIndex: endIndex });

    // Decide next month's start per cross-month rule
    const next = incMonth(y, m);
    const nextY = next[0];
    const nextM = next[1];
    const nextFirstDay = new Date(Date.UTC(nextY, nextM - 1, 1));
    const nextFirstWeekday = nextFirstDay.getUTCDay();
    const crossWithPrev = nextFirstWeekday !== 0; // not Sunday means first week crosses previous month
    startIndex = crossWithPrev ? endIndex - 1 : endIndex;

    y = nextY;
    m = nextM;
  }

  return indices;
}

function generate(startYear, endYear) {
  const { weeks } = buildWeeks(startYear, endYear);
  const monthIndices = computeMonthIndicesAccumulative(weeks);

  // Build output structure: weeks -> days
  const out = weeks.map(week => week.map(d => {
    const y = d.getUTCFullYear();
    const m = d.getUTCMonth() + 1;
    const { startWeekIndex, endWeekIndex } = monthIndices.get(`${y}-${m}`) || { startWeekIndex: 0, endWeekIndex: 0 };
    return {
      year: y,
      month: m,
      day: d.getUTCDate(),
      extraInfo: "",
      monthStartWeekIndex: startWeekIndex,
      monthEndWeekIndex: endWeekIndex,
      shiftType: 0
    };
  }));

  return out;
}

function main() {
  const startYear = parseInt(process.argv[2], 10);
  const endYear = parseInt(process.argv[3], 10);
  const outPath = process.argv[4] || path.join('entry', 'src', 'main', 'resources', 'rawfile', 'calendar_data copy.json');

  if (!Number.isInteger(startYear) || !Number.isInteger(endYear) || startYear > endYear) {
    console.error('Invalid args. Usage: node tools/generateCalendarData.js 2020 2030 \\"entry/src/main/resources/rawfile/calendar_data copy.json\\"');
    process.exit(1);
  }

  const data = generate(startYear, endYear);
  const json = JSON.stringify(data, null, 2);
  fs.mkdirSync(path.dirname(outPath), { recursive: true });
  fs.writeFileSync(outPath, json + '\n', { encoding: 'utf8' });
  console.log(`Wrote ${data.length} weeks to: ${outPath}`);
}

if (require.main === module) {
  main();
}


